package net.cathackers.devel.scmp.chat.impl;

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import org.apache.log4j.Logger;

import net.cathackers.devel.scmp.auth.interfaces.UserID;
import net.cathackers.devel.scmp.chat.interfaces.ChatSession;
import net.cathackers.devel.scmp.chat.interfaces.OnlineSessionsCache;
import net.cathackers.devel.scmp.factories.SCMPFactory;

public class OnlineSessionsCacheImpl implements OnlineSessionsCache {
    /**
     * hash map of UserID -> Hashtable of ChatSessions
     * The Hashtable is resource -> ChatSession
     */
    protected Map<String, Hashtable<String, ChatSession>> treeBackend_;
    protected Map<String, ChatSession> sessionsByID_;
    private Logger logger_;
    public OnlineSessionsCacheImpl() {
	logger_ = SCMPFactory.getLogger(this);
	reset();
    }
    @Override
    public void addSession(ChatSession session) {
	sessionsByID_.put(session.getSessionID(), session);
	if (null != session.getUserID()) {
	    identifySession(session.getSessionID());
	}
    }
    
    @Override
    public boolean checkIfUserIDExists(UserID userID) {
	boolean ret = false;
	ret = treeBackend_.containsKey(userID.getID());
	if (userID.getResource() != null && ret == true) {  
	    Hashtable<String, ChatSession> inner = treeBackend_.get(userID.getID());
	    if (null == inner) {
		ret = false;
	    } else {
		ret = inner.containsKey(userID.getResource());
		logger_.debug("inner contains it "+ ret);
	    }
	}
	return ret;
    }

    @Override
    public ChatSession[] getAllSessions() {
	return sessionsByID_.values().toArray(new ChatSession[sessionsByID_.size()]);
    }

    @Override
    public ChatSession[] getSession(UserID userID) {
	ChatSession[] ret;
	Hashtable<String, ChatSession> inner = treeBackend_.get(userID.getID());
	if (null == inner) {
	    ret = new ChatSession[0];
	} else {
	    if (userID.getResource() == null || !inner.containsKey(userID.getResource())) {
		ret = inner.values().toArray(new ChatSession[inner.size()]);
	    } else {
		 ret = new ChatSession[1];
		 ret[0] = inner.get(userID.getResource());
	    }
	}
	return ret;
    }

    @Override
    public boolean removeSession(ChatSession session) {
	return removeSession(session.getSessionID());
    }

    @Override
    public void reset() {
	
	treeBackend_ 	= Collections.synchronizedMap(new HashMap<String, Hashtable<String,ChatSession>>());
	sessionsByID_ 	= Collections.synchronizedMap(new HashMap<String, ChatSession>()); 
    }
    
    @Override
    public ChatSession getSessionBySessionID(String sessionID) {
	return sessionsByID_.get(sessionID);
    }
    
    @Override
    public void identifySession(String sessionID) {
	ChatSession toBeIdentified = sessionsByID_.get(sessionID);
	Hashtable<String, ChatSession> inner = treeBackend_.get(toBeIdentified.getUserID().getID());
	if (null == inner) {
	    //add session to the mapping
	    Hashtable<String, ChatSession> newHashtable = new Hashtable<String, ChatSession>();
	    treeBackend_.put(toBeIdentified.getUserID().getID(), newHashtable);
	    newHashtable.put(toBeIdentified.getUserID().getResource(), toBeIdentified);
	} else {
	    inner.put(toBeIdentified.getUserID().getResource(), toBeIdentified);
	}
    }
    
    @Override
    public boolean checkIfSessionIDExists(String sessionID) {
	return sessionsByID_.containsKey(sessionID);
    }
    
    @Override
    public boolean removeSession(String sessionID) {
	boolean ret = false;
	ChatSession oldValue = sessionsByID_.remove(sessionID);
	if (oldValue == null) {
	    ret = false;
	} else {
	    //do best-effort to remove the session from the tree
	    Hashtable<String, ChatSession> inner = treeBackend_.get(oldValue.getUserID().getID());
	    if (null != inner) {
		assert inner.containsKey(oldValue.getUserID().getResource()) == true;
		inner.remove(oldValue.getUserID().getResource());
		ret = true;
		if (inner.isEmpty()) {
		    // remove it from parent
		    treeBackend_.remove(oldValue.getUserID().getID());
		    logger_.debug("All sessions of user " + oldValue.getUserID().getID() + " has been removed");
		}
	    }
	}
	return ret;
    }
    @Override
    public int size() {
	return sessionsByID_.size();
    }
    
}
